"""
A set of functions for retrieving previous instant message conversations.
All functions return an interable whose items are tuples in the form of:
(date/time,screenname,message)
"""

import os,re
import itertools
from datetime import datetime

recordre=re.compile(r'^\((\d\d?\:\d\d\:\d\d(\s+[A|P]M)?)\)\s+(.*?)\:\s+(.*)\s*$', re.I|re.L)



class PidginLogRecord(object):
    """Container for a single entry/line in a pidgin log file"""
    
    _Msg=None
    _SenderScrName=None
    _MsgTime=None

    def __init__(self,Message, SenderScrName, MsgTime):
        self._Msg=Message
        self._SenderScrName=SenderScrName
        self._MsgTime=MsgTime

    msg=property(lambda self: self._Msg)
    sender=property(lambda self: self._SenderScrName)
    msg_time=property(lambda self: self._MsgTime)

    @classmethod
    def fromRawRecord(cls,RawRecord,DayOfYearStr):
        m=recordre.search(RawRecord.strip())
        if not m:
            raise Exception(RawRecord)
        tod,isampm,scrnname,msg=m.groups()
        if isampm:
            dtime=datetime.strptime('%s %s'%(DayOfYearStr,tod), '%Y-%m-%d %I:%M:%S %p')	
        else:
            dtime=datetime.strptime('%s %s'%(DayOfYearStr,tod), '%Y-%m-%d %H:%M:%S')
        return cls(msg,scrnname,dtime)


class PidginLog(object):
    _Open=open

    _LogOwner=None
    _LogDate=None
    _LogRecords=None

    log_date=property(lambda self: self._LogDate)
    log_records=property(lambda self: iter(self._LogRecords))
    
    def __init__(self,LogRecords,LogDate):
        self._LogRecords=LogRecords
        self._LogDate=LogDate

    def __iter__(self):
        return self.log_records

    @classmethod
    def fromPath(cls,PathToLog):
        """Create a PidginLog object from the path to a log file"""
        doy=cls.pathToDate(PathToLog)
        #owner=cls.pathToOwner(PathToLog)
        #reciever=cls.pathToReciever(PathToLog)
        fobj=cls._Open(PathToLog,'r')
        log_obj=cls.fromFileObj(fobj,doy)
        fobj.close()
        return log_obj

    @classmethod
    def fromFileObj(cls,FileObj,DayOfYear):
        FileObj.readline() #Dump top line which doesn't have a conversation record
        records=[]
        rcrd_lines=(l for l in FileObj.readlines() if l.strip())
        records=map(lambda fline: PidginLogRecord.fromRawRecord(fline, DayOfYear), rcrd_lines)
        return cls(records,DayOfYear)
                    
    @staticmethod
    def pathToDateStr(LogPath):
        """Extract a date value from path to a pidgin log"""
        base_fname=os.path.basename(LogPath)
        fname,extenstion=base_fname.rsplit('.',1)
        return fname.split('.')[0]

    @staticmethod
    def pathToOwner(LogPath):
        dir_path=os.path.dirname(LogPath)
        dir_path=os.path.dirname(dir_path)
        fname=os.path.basename(dir_path)
        return fname

    @staticmethod
    def pathToReciever(LogPath):
        dir_path=os.path.dirname(LogPath)
        fname=os.path.basename(dir_path)
        return fname

    
        
class PidginLogParser(object):
    """Acquires all conversations by parsing Pidgin log files"""

    _LogItems=None

    logs=property(lambda self: iter(self._LogItems))

    def __init__(self,LogFiles):
        """
        LogFiles: Iterable of PidginLog objects
        """
        self._LogItems=LogFiles

    def iterRecords(self):
        """
        Iterator of all records in all the logs
        """
        return itertools.chain(*list(self._LogItems))
   
    @classmethod
    def fromDirectory(cls,LogDir):
        """
        Creates a PidginLogParser object from path to a directory
        containing pidgin log files
        """
        assert os.path.isdir(LogDir), 'Not a directory: '+str(LogDir)
        log_objs=map(PidginLog.fromPath, cls.filesInSubDirectories(LogDir))
        return cls(log_objs)
            
    @staticmethod
    def filesInSubDirectories(DirPath):
        mkpath=lambda filename: os.path.join(LogDir,filename)
        for d in [mkpath(f) for f in os.listdir(LogDir) if os.path.isdir(mkpath(f))]:
            for f in os.listdir(d):
                yield mkpath(d,f)



def PidginLogs(LogDir):
	"""Returns all conversations by parsing Pidgin log files"""
	assert os.path.isdir(LogDir), 'Not a directory: '+str(LogDir)
	messages=[]
	for d in [LogDir+os.sep+f for f in os.listdir(LogDir) if os.path.isdir(LogDir+os.sep+f)]:
		for logf in os.listdir(d):
			doy=logf.split('.')[0]
			logf=d+os.sep+logf
			for rec in open(logf).readlines()[1:]:
				t=_ParsePidginRecord(rec,doy)
				if t:
					messages.append(t)
				elif not _IsSystemMessage(rec):
					pass
					#print "malformed log record '%s'"%rec
					#raise ValueError("malformed log record '%s' in file '%s'"%(rec,logf))
	return messages


def _ParsePidginRecord(recordtext,dayofyear):
	m=recordre.search(recordtext.strip())
	if not m:
		return
	tod,isampm,scrnname,msg=m.groups()
	if isampm:
		dtime=datetime.strptime('%s %s'%(dayofyear,tod), '%Y-%m-%d %I:%M:%S %p')	
	else:
		dtime=datetime.strptime('%s %s'%(dayofyear,tod), '%Y-%m-%d %H:%M:%S')
	return (dtime,scrnname,msg,)

_SysMsgSuffixes=[
	'has signed off.',
	'has signed on.',
	'logged out.',
	'logged in.',
	'is no longer idle.',
	'has become idle.',
	'has gone away.',
	'is no longer away.',
	'entered the room.',
	'left the room.',
	]

def _IsSystemMessage(recordtext):
	recordtext=recordtext.strip()
	for sfx in _SysMsgSuffixes:
		if recordtext.endswith(sfx):
			return True
	return False
	
#def _loadmap(self, filename):
	#each entry in the word map is a (precision+1)-tuple
	#each word of the precision, plus the number of occurrences
	#wordmap={}
	
	# when using the new, loaded map, use (precision-1) tokens, and get the possible next ones and their frequency, and randomly choose one given the frequency
	
	#self._precision= FROM FILE
	
	#for line in file 
	#	
